#include "view.h"
#include <allegro5/allegro5.h>
#include <allegro5/allegro_primitives.h>
#include <cassert>
#include "font.h"

View::View(Rect r, Font *pFont)
: m_needPaint(true), m_grabMouse(false), m_visible(true), m_rect(r),
  m_pFont(pFont), m_oldMousePos(0,0), m_needFlip(false),
  m_grabKeys(false), m_hasFocus(false) {
}

Font* View::font() {
     return m_pFont;
}

void View::setFont(Font *pFont) {
     assert(pFont);
     m_pFont = pFont;
}

bool View::needPaint() {
     for (unsigned int i=0; i<m_views.size(); i++) {
	  if (m_views[i]->needPaint())
	       m_needPaint = true;
     }
     return m_needPaint;
}

void View::clear() {
     // clear
     al_draw_filled_rectangle(m_rect.x(), m_rect.y(), 
			      m_rect.x() + m_rect.width(), 
			      m_rect.y() + m_rect.height(),
			      al_map_rgb(0,0,0));
}

void View::paint(bool all) {
     if (m_needPaint)
	  all = true;
     if (all)
	  clear();
     // repaint any individual views which need painting
     for (unsigned int i=0; i<m_views.size(); i++) {
	  m_views[i]->paint(all);
	  m_needFlip = true;
     }
     m_needPaint = false;
}

void View::keyDown(int keycode, int modifiers, int unichar) {
     // check for key grab
     for (unsigned int i=0; i<m_views.size(); i++) {
	  if (m_views[i]->grabKeys()) {
	       m_views[i]->keyDown(keycode, modifiers, unichar);
	       return;
	  }
     }
     // otherwise pass to all child views
     for (unsigned int i=0; i<m_views.size(); i++)
	  m_views[i]->keyDown(keycode, modifiers, unichar);
}

bool View::grabKeys() {
     for (unsigned int i=0; i<m_views.size(); i++) {
	  if (m_views[i]->grabKeys())
	       return true;
     }
     return m_grabKeys;
}

void View::mouseDown(Point p, int button) {
     for (unsigned int i=0; i<m_views.size(); i++) {
	  if (m_views[i]->rect().inside(p))
	       m_views[i]->mouseDown(p, button);
     }
     focus();
}

void View::mouseUp(Point p, int button) {
     for (unsigned int i=0; i<m_views.size(); i++) {
	  if (m_views[i]->rect().inside(p))
	       m_views[i]->mouseUp(p, button);
	  else if (m_views[i]->hasFocus())
	       m_views[i]->blur();
     }
}

void View::mouseMove(Point p, Point dp) {
     bool grab = false;
     for (unsigned int i=0; i<m_views.size(); i++) {
	  if (m_views[i]->grabMouse()) {
	       // only allow one grab
	       m_views[i]->mouseMove(p, dp);
	       grab = true;
	       break;
	  }
     }
     if (!grab) {
	  for (unsigned int i=0; i<m_views.size(); i++) {
	       if (m_views[i]->rect().inside(p) &&
		   m_views[i]->rect().inside(m_oldMousePos))
		    m_views[i]->mouseMove(p, dp);
	       else if (m_views[i]->rect().inside(p))
		    m_views[i]->mouseEnter(p);
	       else if (m_views[i]->rect().inside(m_oldMousePos))
		    m_views[i]->mouseExit(m_oldMousePos);
	  }
     }
     m_oldMousePos = p;
}

void View::mouseEnter(Point p) {
     for (unsigned int i=0; i<m_views.size(); i++) {
	  if (m_views[i]->rect().inside(p))
	       m_views[i]->mouseEnter(p);
     }
}

void View::mouseExit(Point p) {
     for (unsigned int i=0; i<m_views.size(); i++) {
	  if (m_views[i]->rect().inside(m_oldMousePos))
	       m_views[i]->mouseExit(m_oldMousePos);
     }
}

bool View::grabMouse() {
     bool grab = m_grabMouse;
     for (unsigned int i=0; i<m_views.size(); i++) {
	  if (m_views[i]->grabMouse())
	       grab = true;
     }
     return grab;
}

void View::refresh() {
     m_needPaint = true;
}

void View::show() {
     m_visible = true;
}

void View::hide() {
     m_visible = false;
}

Rect View::rect() const {
     return m_rect;
}

void View::setRect(Rect r) {
     m_rect = r;
     refresh();
}

void View::update() {
     for (unsigned int i=0; i<m_views.size(); i++)
	  m_views[i]->update();
}

bool View::visible() {
     return m_visible;
}

bool View::hasFocus() {
     return m_hasFocus;
}

void View::focus() {
     m_hasFocus = true;
     for (unsigned int i=0; i<m_views.size(); i++) {
	  if (!m_views[i]->hasFocus())
	       m_views[i]->focus();
     }
}

void View::blur() {
     m_hasFocus = false;
     for (unsigned int i=0; i<m_views.size(); i++) {
	  if (m_views[i]->hasFocus())
	       m_views[i]->blur();
     }
}
